package com.xlabs.server.DO;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.users.User;
import com.xlabs.client.DTO.QuestionDTO;
import com.xlabs.client.DTO.TransferDTO;

/**
 * Question data object.
 */
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class QuestionDO
{
	/**
	 * Primary key.
	 */
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Key key;

	/**
	 * The text comment.
	 */
	@Persistent
	private String questionText;

	/**
	 * The start time of video to which this question relates in seconds.
	 */
	@Persistent
	private Integer startTime;

	/**
	 * The end time of video to which this question relates.
	 */
	@Persistent
	private Integer endTime;

	/**
	 * The video that has this comment.
	 */
	@Persistent
	private Key video;

	/**
	 * Replies to this comment.
	 */
	@Persistent
	private List<Key> replies;

	/**
	 * When created.
	 */
	@Persistent
	private Date createdOn;

	/**
	 * Created by user.
	 */
	@Persistent
	private User createdBy;
	
	/**
	 * Index for sorting based on question's start time.
	 */
	@Persistent
	private String startTimeIndex;

	/**
	 * Initializes an instance of TimedComment.
	 * 
	 * @param questionText The comment.
	 * @param startTime the start of corresponding video interval.
	 * @param endTime  the end of corresponding video interval.
	 * @param user User that added this question
	 */
	public QuestionDO(String questionText, int startTime, int endTime, Date createdOn, User user)
	{
		this.questionText = questionText;
		this.startTime = startTime;
		this.endTime = endTime;
		this.createdOn = createdOn;
		this.createdBy = user;
		this.startTimeIndex = ((leftPad(startTime, 6) + "") + "|" + createdOn + "|" + user.getEmail()); 
	}

	/**
	 * Adds padding 0 to the left of the string representation to make 6 digits.
	 * @param n
	 * @param digits number of digits.
	 * @return
	 */
	private String leftPad(int n, int digits)
	{
		String result = String.valueOf(n);
		int zerosToAdd = digits - result.length();
		
		for (int nIdx = 0; nIdx < zerosToAdd; nIdx ++)
		{
			result = "0" + result;
		}
		
		return result;
	}

	/**
	 * Gets the key.
	 * 
	 * @return id.
	 */
	public Key getKey()
	{
		return key;
	}

	/**
	 * Sets the key.
	 * 
	 * @param key
	 *            key.
	 */
	public void setKey(Key key)
	{
		this.key = key;
	}

	/**
	 * Gets comment.
	 * 
	 * @return comment.
	 */
	public String getQuestionText()
	{
		return questionText;
	}

	/**
	 * Sets Comment.
	 * 
	 * @param questionText
	 *            The text comment.
	 */
	public void setQuestionText(String questionText)
	{
		this.questionText = questionText;
	}

	/**
	 * Gets video.
	 * 
	 * @return video.
	 */
	public Key getVideo()
	{
		return video;
	}

	/**
	 * Sets video
	 * 
	 * @param video
	 *            video that has this comment.
	 */
	public void setVideo(Key video)
	{
		this.video = video;
	}

	/**
	 * Gets replies.
	 * 
	 * @return List of replies.
	 */
	public List<Key> getReplies()
	{
		return replies;
	}

	/**
	 * Sets replies.
	 * 
	 * @param replies
	 *            List of replies.
	 */
	public void setReplies(List<Key> replies)
	{
		this.replies = replies;
	}

	/**
	 * Adds a reply.
	 * 
	 * @param replyKey
	 *            reply key.
	 */
	public void addReply(Key replyKey)
	{
		if (this.replies == null)
		{
			this.replies = new ArrayList<Key>();
		}
		this.replies.add(replyKey);
	}

	/**
	 * Returns the encoded Key of this JDO as a string.
	 * 
	 * @return encoded key.
	 */
	public String getEncodedKey()
	{
		return KeyFactory.keyToString(this.key);
	}

	/**
	 * Returns a range of questions sorted by time
	 * 
	 * @param fromIndexIncl
	 *            The start index of range.
	 * @param toIndexExcl
	 *            The end index of range.
	 * @return result. INEFFICIENT ON APP ENGINE - RUN TIME GROWS LINEAR WITH
	 *         VALUE OF FROMINDEXINCL.
	 */
	@SuppressWarnings("unchecked")
	public static QuestionDTO[] getQuestionsByIndexRange(long fromIndexIncl,
			long toIndexExcl)
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try
		{
			String allQuestionsQuery = "SELECT FROM "
					+ QuestionDO.class.getName() + " ORDER BY startTime ASC ";
			Query qQuery = pm.newQuery(allQuestionsQuery);
			qQuery.setRange(fromIndexIncl, toIndexExcl);
			List<QuestionDO> allQuestions = (List<QuestionDO>) qQuery.execute();
			return QuestionDO.toQuestionDTOArray(allQuestions);
		} finally
		{
			pm.close();
		}
	}

	/**
	 * Returns all questions that fall in (time - length, time + length)
	 * interval.
	 * 
	 * @param time
	 *            center of interval.
	 * @param length
	 *            half of interval's length.
	 * @return all questions that fall in (time - length, time + length)
	 *         interval.
	 */
	@SuppressWarnings("unchecked")
	public static QuestionDTO[] getQuestionsInSymmetricTimeInterval(int time,
			int length)
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();

		try
		{
			String questionsInRangeQuery = "SELECT FROM "
					+ QuestionDO.class.getName()
					+ " where startTime >= :rangeStart && startTime <= :rangeEnd "
					+ " ORDER BY startTime ASC ";
			Query query = pm.newQuery(questionsInRangeQuery);
			List<QuestionDO> questionList = (List<QuestionDO>) query.execute(
					time - length, time + length);

			return QuestionDO.toQuestionDTOArray(questionList);
		} finally
		{
			pm.close();
		}
	}

	/**
	 * Gets a page of questions that are asked in the given time index interval.
	 * 
	 * @param startTimeIndexIncl
	 *            start time index.
	 * @param endTimeIndexExcl
	 *            end time index.
	 * @param direction
	 *            specifies the direction of query.
	 * @param pageSize
	 *            Size of a page.
	 * @param pm
	 *            persistence manager
	 * @return Object array including list of questions and query cursor.
	 */
	@SuppressWarnings("unchecked")
	public static QuestionQueryResult queryQuestionPageWithStartTimeInInterval(
			String startTimeIndexIncl, 
			String endTimeIndexExcl, 
			int direction, 
			int pageSize,
			PersistenceManager pm)
	{
		String questionsQuery = "SELECT FROM "
				+ QuestionDO.class.getName()
				+ " where startTimeIndex >= :intervalStart && startTimeIndex < :intervalEnd"
				+ " ORDER BY startTimeIndex "
				+ (direction == TransferDTO.RIGHT_DIRECTION ? " ASC " : " DESC ");

		Query query = pm.newQuery(questionsQuery);
		query.setRange(0, pageSize);

		List<QuestionDO> questionList = (List<QuestionDO>) query.execute(startTimeIndexIncl, endTimeIndexExcl);
		
		return new QuestionQueryResult(questionList);
	}

	/**
	 * Gets createdOn.
	 * 
	 * @return time of creation.
	 */
	public Date getCreatedOn()
	{
		return createdOn;
	}

	/**
	 * Gets createdBy.
	 * 
	 * @return creator.
	 */
	public User getCreatedBy()
	{
		return createdBy;
	}

	/**
	 * Gets {@link #startTime}
	 * 
	 * @return {@link #startTime}
	 */
	public Integer getStartTime()
	{
		return startTime;
	}

	/**
	 * Gets {@link #endTime}
	 * 
	 * @return {@link #endTime}
	 */
	public Integer getEndTime()
	{
		return endTime;
	}

	/**
	 * Converts an array of {@link com.xlabs.server.DO.QuestionDO} objects to an
	 * array of {@link QuestionDTO}
	 * 
	 * @param questionDOList
	 * @return
	 */
	@Deprecated
	public static QuestionDTO[] toQuestionDTOArray(
			List<QuestionDO> questionDOList)
	{
		QuestionDTO[] questionDTOs = new QuestionDTO[questionDOList.size()];

		int index = 0;
		for (QuestionDO aQuestionDO : questionDOList)
		{
			questionDTOs[index++] = aQuestionDO.toQuestionDTO();
		}

		return questionDTOs;
	}

	public static List<QuestionDTO> toDTOList(List<QuestionDO> doList)
	{
		List<QuestionDTO> dtoList = new ArrayList<QuestionDTO>();

		for (QuestionDO questionDO : doList)
		{
			dtoList.add(questionDO.toQuestionDTO());
		}

		return dtoList;
	}

	/**
	 * Converts this object to CommentDTO.
	 * 
	 * @return com.xlabs.client.CommentDTO
	 */
	public QuestionDTO toQuestionDTO()
	{
		QuestionDTO dto = new QuestionDTO(getQuestionText(), getStartTime(), getEndTime(), getCreatedOn(), getStartTimeIndex());

		dto.setEncodedKey(this.getEncodedKey());
		dto.setCreatedBy(this.getCreatedBy().getNickname());

		return dto;
	}
	
	/**
	 * see {@link Object}{@link #toString()}
	 */
	public String toString()
	{
		return "startTime : " + startTime + " questionText: " + questionText;
		
	}

	/**
	 * Getter for {@link #startTimeIndex}
	 * @return the {@link #startTimeIndex}
	 */
	public String getStartTimeIndex() 
	{
		return startTimeIndex;
	}
}
